<?php
/**
 *默认实现 get post put delete 四个方法，另加自定义api
 *
 *sys中参数有 sys_route, 
 *           sys_ajax, 
 *           sys_dataType, 
 *           sys_tpl, 
 *           sys_debug, 
 *           sys_refresh
 *           sys_debug_log
 *           sys_rewrite
 *
 *@package 
 *@copyright Copyright (c) 2012-2014 Tuniu.com
 *@author huxiaomin 2012
 *@version 1.0
 */
  
class TApp{
    
	private $app_arr = array();//app信息
    public $sys;   // sys参数
    
    private $api_config = array();//api 设置
    private $app;   //app实例
    private $app_config = array(
                'tpl' => '',
                'no_tpl'=>0,
                'rewrite'=> '',
                'debug_log'=>'',
                'ajax'=>'0',
                'dataType'=>'',
                'debug'=>DEBUG_TYPE,
                'cache'=>'0',
                'cache_time'=>'3600',
                'cache_type'=>'self',
            ); //app配置
    private $ob = '';//api最终输出
    private $header = array();//header输出
    private $return = array();//api返回
    private $debug_ob = '';//debug输出
    private $exec_time = 0;// 执行时间
    public  $useTCache = 0;//是否使用到文件缓存
    
    function __construct(){
        
    }
    
    /**
     *          $app_arr=   array(
     *               'pwd' => $pwd,
                     'dir' => "{$folder}/".$file,
                     'folder'=>$folder,
                     'class'=>$file,
                     'class_name'=>$class_name,
                     'api' => $api,
                     );
     * @param  $app_arr
     */
    static function start($app_arr, &$request, $sys){
        if (is_string($app_arr)){
           $app_arr = TRestful::parseUri($app_arr);
        }
        $tapp = new TApp();
        $tapp->ini($app_arr,$sys);
        $tapp->check();
        $tapp->hook();
        $tapp->setAppConfig();
        //相当于before
        TPlugin::start('beforeTapp',$tapp);
        $tapp->startSys($request);
        //相当于after
        TPlugin::start('afterTapp',$tapp);
        return $tapp;
    }
    
    /**
     * 
     * @param array $request
     * @return string ob
     */
    function startSys(&$request){
        $time = gtime();
        //--------------------自带缓存机制-------------------//
        if ($this->app_config['cache']){
            TCache::start($this->app_config['cache_time'],$this->app_config['cache_type']);
            $dir = $this->app_arr['folder'].'/'.$this->app_arr['class'].'/'.$this->app_arr['api'];
	        $cache = TCache::getCache($request,$dir);
	        if ($cache['success'] ){
	            $this->useTCache = 1;
	        	$this->ob = $cache['data'];
	        	$this->return = $cache['extra'];
	        	//----debug--//
	        	$this->exec_time = gtime() - $time;
	        	$debug_log = "\n".date('Y-m-d H:i:s')."\n"
                        ."execute_time:".$this->exec_time."\n"
                        .'app_arr:'.print_r($this->app_arr,1)."\n"
                        .'app_config:'.print_r($this->app_config,1)."\n"
                        .'use_cache:'.$dir."\n"
                        .'return:'.print_r($this->return, 1);
                TDebug::add($debug_log);
                TCache::end();
	            return $cache['data'];
	        }
        }
        TCache::addFile($this->app_arr['pwd']);
        //--------------------正常执行-----------------------//
        ob_start();
        $return = $this->getApi($request);
        if ($this->app_config['no_tpl']){
            echo $return['data'];
        }
        elseif ($this->app_config['ajax']){
            echo $this->getAjax($return);
        }
        elseif ($this->app_config['tpl']){
            $this->getTpl($return,$request);
        }
        else {
            echo $return['data'];
        }
        $this->ob = ob_get_clean();
        $this->return = $return['data'];
        
        $this->sql_log = DDAO::$sql_log;
        DDAO::$sql_log = array();
        $this->sql_error = DDAO::$error;
        DDAO::$error = array();
        
        //--------------------自带缓存机制-------------------//
        if ($this->app_config['cache']){
        	TCache::setCache($this->ob,$dir,$return['data']);
        	TCache::end();
        }
        $this->exec_time = gtime()-$time;
        
        //---------------------自带debug信息机制-------------//
        $debug_log = "\n".date('Y-m-d H:i:s')."\n"
                        ."execute_time:".$this->exec_time."\n"
                        .'app_arr:'.print_r($this->app_arr,1)."\n"
                        .'sql_log:'.print_r($this->sql_log,1)."\n"
                        .'app_config:'.print_r($this->app_config,1)."\n"
                        .'sql_error:'.print_r($this->sql_error,1)."\n";
        if ($this->api_config['cache']){
             $debug_log .='cache_msg:'.$cache['msg'].'success'.$cache['success']." \n";
        }
        $debug_log .='return:'.print_r($return, 1);
        TDebug::add($debug_log);
        
        return $this->ob;
    }
    
    
    /**
     * 初始化
     * 
     */
    function ini($app_arr,$sys=array()){
        $this->app_arr = $app_arr;
        $this->sys = $sys;
        $this->app_config['tpl'] = "{$this->app_arr['folder']}/{$this->app_arr['class']}.{$this->app_arr['api']}.html";
    }
    
    /**
     * 检验参数合法。
     */
    function check(){
        TThrow::assert(empty($this->app_arr['api']) , 'api is empty', 'die');
        TThrow::assert(empty($this->app_arr['class_name']), 'class is empty', 'die');
        TThrow::assert( !file_exists($this->app_arr['pwd']), 'file not exists', 'die');
        $dir = $this->app_arr['pwd'];
        require_once $dir;
        $class = $this->app_arr['class_name'];
        $this->app =  new $class();
        $this->api = $this->app_arr['api'];
        TThrow::assert(!is_callable(array($this->app,$this->app_arr['api'])), 'api isnot callable', 'die');
    }
    
    /**
     * 检测是否有插件
     */
    function hook(){
        $api_config = array();
        if (is_callable(array($this->app,'hook'))){
            $api_config = $this->app->hook($this->app_arr['api']);  
        }
        return $this->api_config = $api_config;
    }
    
    /**
     * 初始化config
     */
    function setAppConfig(){
    	$this->app_config = array_merge(
    	                                (array)$this->app_config,
    	                                (array)$this->api_config
    	                                );
    	foreach ((array)$this->sys as $key=>$value){
    	   $new_key = substr($key,4);
    	   $this->app_config[$new_key] = $value;
    	}
    	return $this->app_config;
    }
    
    /**
     * 执行api
     * //TODO 可以获取api执行后的配置信息
     */
    function getApi(&$request){
           $app = $this->app;
           $api = $this->app_arr['api'];
           
           
           $return = array();
           $return['success'] = 1;
           ob_start();
           try {
               $return['data'] = $app->$api(&$request);
           }catch(Exception $e){
               $return['app_exception'] = $e;
               $return['success'] = 0;
           }
           $ob = ob_get_clean();
           $return['app_ob'] = $ob; 
           return $return;      
    }
    
    /**
     * ajax 处理
     */
    function getAjax($return){
        $sys_dataType = '';
        if ($this->app_config['ajax']){
               switch ($this->app_config['dataType']){
                    case 'txt':
                        $sys_dataType = $return['data'];
                        break;
                    case 'json':
                        $sys_dataType = json_encode($return['data']);
                        break;
                    case 'xml':
                        //TODO 将数组转换成xml
                        break;
                    default:
                        $sys_dataType = $return['data'];
                        break;
               }
              return $sys_dataType;
           }
    }
    
    /**
     * 使用模板渲染 
     */
    function getTpl($return, &$request){
         
         //TODO die vs. throw 需要考虑下。
         try {
            TThrow::assert(!file_exists(TTPL_DIR.$this->app_config['tpl']), 'miss html: '.$this->app_config['tpl']);
         }catch (Exception $e){
            echo $return['app_ob'];
            return $return;
         }
         
         TTpl::start($this->app_config['tpl'], $return, $request);
         return $return;
    }
    
   function __get($name){
   	  if (!isset($this->$name)){
   	    TLog::logTemp('__get fail '.$name.' not exists','temp',2);
   	    return false;
   	  }
   	  return $this->$name;
   }
    
    /**
     * 设置输出
     * @param string $ob      输出
     * @param boolean $append 是否追加
     */
    function setOb($ob,$append=1){
        if ($append){
           $this->ob .= $ob;
        }
        else {
           $this->ob  = $ob;    
        }
    }

}